#ifndef __BASE_MD5_H__
#define __BASE_MD5_H__
#pragma once

#include <string>
#include <limits>

namespace butils
{

class md5
{
public:
	md5();
	md5(const std::string& message);
	md5(const char* message);
	md5(const void* message, std::size_t size);

	std::string str(bool upper = false);

	void process_byte(unsigned char byte);

	void process_bytes(void const* buffer, std::size_t byte_count);

	void get_digest(std::uint8_t digest[16]);

private:
	typedef uint32_t MD5_u32plus;

	typedef struct {
		MD5_u32plus lo, hi;
		MD5_u32plus a, b, c, d;
		unsigned char buffer[64];
		MD5_u32plus block[16];
	} MD5_CTX;

	inline MD5_u32plus UUID_DETAIL_MD5_F(MD5_u32plus x, MD5_u32plus y, MD5_u32plus z) { return ((z) ^ ((x) & ((y) ^ (z)))); }
	inline MD5_u32plus UUID_DETAIL_MD5_G(MD5_u32plus x, MD5_u32plus y, MD5_u32plus z) { return ((y) ^ ((z) & ((x) ^ (y)))); }
	inline MD5_u32plus UUID_DETAIL_MD5_H(MD5_u32plus x, MD5_u32plus y, MD5_u32plus z) { return (((x) ^ (y)) ^ (z)); }
	inline MD5_u32plus UUID_DETAIL_MD5_H2(MD5_u32plus x, MD5_u32plus y, MD5_u32plus z) { return ((x) ^ ((y) ^ (z))); }
	inline MD5_u32plus UUID_DETAIL_MD5_I(MD5_u32plus x, MD5_u32plus y, MD5_u32plus z) { return ((y) ^ ((x) | ~(z))); }

	const void *body(MD5_CTX *ctx, const void *data, unsigned long size);

	void MD5_Init(MD5_CTX *ctx);
	void MD5_Update(MD5_CTX *ctx, const void *data, unsigned long size);
	void MD5_Final(unsigned char *result, MD5_CTX *ctx);

private:
	MD5_CTX ctx_;
};

}

#endif //__BASE_CRYPT_H__